#include <rpc_root.h>
#include <memory>
#include "mock_transport.h"
#include "mock_caller.h"
#include "mock_logger.h"
#include <knet.h>
#include <list>
#include <iostream>


using CallerList = std::list<MockCaller*>;
CallerList callerList;

std::unique_ptr<rpc::RpcImpl> rpc_impl;

void client_cb(kchannel_ref_t* channel, knet_channel_cb_event_e e) {
    if (!rpc_impl) {
        abort();
        return;
    }
    if (e & channel_cb_event_recv) {
        auto ptr = rpc::make_shared_ptr<MockTrans>(channel);
        //rpc_impl->onMessage(ptr);
        rpc_impl->onMessage(std::dynamic_pointer_cast<rpc::Transport>(ptr));
    }
}

void acceptor_cb(kchannel_ref_t* channel, knet_channel_cb_event_e e) {
    if (e & channel_cb_event_accept) {
        std::cout << "accepted" << std::endl;
        knet_channel_ref_set_cb(channel, client_cb);
    }
}

void connector_cb(kchannel_ref_t* channel, knet_channel_cb_event_e e) {
    if (!rpc_impl) {
        abort();
        return;
    }

    if (e & channel_cb_event_connect) {
        std::cout << "connected" << std::endl;
        auto ptr = rpc::make_shared_ptr<MockTrans>(channel);
        auto* callerPtr = new MockCaller();
        callerList.push_back(callerPtr);
        try {
            callerPtr->doTest(ptr, rpc_impl.get());
        }
        catch (const std::exception& e) {
            rpc_impl->getLogger()->write(e.what());
        }

    } else if (e & channel_cb_event_recv) {
        auto ptr = rpc::make_shared_ptr<MockTrans>(channel);
        rpc_impl->onMessage(std::dynamic_pointer_cast<rpc::Transport>(ptr));
    }
}

int main() {
    std::cout << "start" << std::endl;
    MockLogger logger;
    // create rpc instance 
    rpc_impl = std::make_unique<rpc::RpcImpl>();
    if (rpc_impl == nullptr) {
        logger.write("[RpcMock] init rpc frame work error !!!!!");
        exit(-1);
    }
    //ignore create descriptor for service
    rpc_impl->initialize(&logger);

    std::string soPath;
    // The UUID of 'ServiceDynamic'
    std::string serviceUUID = "5871407834711899994";
#if defined(_WIN32) || defined(_WIN64)
    #ifdef _DEBUG
        soPath = "..\\..\\lib\\stub\\example\\ServiceDynamic\\Debug\\libServiceDynamic.so";
    #else
        soPath = "..\\..\\lib\\stub\\example\\ServiceDynamic\\Release\\libServiceDynamic.so";
    #endif
#else
    soPath = "../lib/stub/example/ServiceDynamic/libServiceDynamic.so";
#endif
    // Try to load shared object for 'ServiceDynamic'
    if (!rpc::loadClass(rpc_impl.get(), serviceUUID, soPath, false, nullptr)) {
        std::cout << "load " << soPath << " failed" << std::endl;
        return 0;
    }

    auto* loop = knet_loop_create();
    auto* acceptor = knet_loop_create_channel(loop, 1024, 1024 * 256);
    knet_channel_ref_set_cb(acceptor, acceptor_cb);
    knet_channel_ref_accept(acceptor, "127.0.0.1", 2344, 512);
    kchannel_ref_t* connector = knet_loop_create_channel(loop, 1024, 1024 * 256);
    knet_channel_ref_set_cb(connector, connector_cb);
    knet_channel_ref_connect(connector, "127.0.0.1", 2344, 2);
    for (;;) {
        knet_loop_run_once(loop);
        rpc_impl->update();
        for (auto& caller : callerList) {
            try {
                caller->doTest(nullptr, rpc_impl.get());
            }
            catch (const std::exception& e) {
                logger.write(e.what());
            }
        }
    }
    return 0;
}
